local skynet = require "skynet"
local socket = require "socket"
local packdeal = require "packdeal"
require "skynet.manager"	-- import skynet.register

require "config_redis"
local ErrNo  = require "common/ErrNo"
local NetCmd = require "common/NetCmd"
local Dealer = require "common/mj_dealer"
local Player = require "common/mj_player"
local MJLogic = require "common/MJLogic"

local json   = require "cjson"
local Common = require "common/Common"


local send_package = packdeal.send_package
local unpack_package = packdeal.unpack_package

local MAX_PLAYER_NUM        = 4

local STATUS_FREE           = 0   --等待开始
local STATUS_CALL           = 100 --叫分状态
local STATUS_PLAY           = 101 --游戏状态
local STATUS_RESULT			= 102 --算分状态

local WAIT_CARD_TIME        = 400000   -- 等待叫牌时间
local OUT_CARD_TIME         = 500000    -- 出牌时间
local WAIT_ANIMAT_TIME		= 10
local jiesan_timer_id		= nil
local has_cal_huitou 		= false
local start_shaizi 			= 0

CMD = {}
REQUEST = {}
RESPOND = {}
local room_id = nil            
local count 	= 0            
local cur_ju	= 1			
local no_start	= 0					
local chairs = {}              
local dealer = Dealer.new(0)   
local status = STATUS_FREE     
local majiang_type	= 1	
local optionType	= nil
local ruleType	= 0	
local gang_can_add_card	= 0	
local rule_type = nil	
local dizhu			= 1
local total_ju	= nil	
local room_type = 1		
local zhengjing = nil
local fujing = nil
local xia_zhengjing = nil
local xia_fujing = nil
local room_is_double = 1
local log_data_id = nil


local cur_id = nil
local host_id_old = nil
local last_out_card = {}
local animats = {}
local pending_user = {}
	
local OPER_EMPTY    		= 0   
local OPER_OUT_CARD 		= 1   
local OPER_PENG     		= 2   
local OPER_GANG     		= 3   
local OPER_CHI_CARD 		= 4   
local OPER_CHI_HU   		= 5   
local OPER_HU      	 		= 6   
local OPER_FINISH_ANIMAT	= 7 
local OPER_JINGFEN			= 8 
local OPER_RESULT			= 9	
local OPER_HUITOU			= 10
local OPER_CAN_CHI_CARD		= 100
local g_oper_card
local time_out_stop_room = 0
local is_one = 0

local JT_SXF		= 1
local JT_SXF_HTX	= 2
local JT_MDL		= 3
local JT_MDL_HTX	= 4

function reset_game(host_id, finish)
    for i=1,MAX_PLAYER_NUM do
        if chairs[i] then
            chairs[i].player:reset_game(finish)
			if finish then
				skynet.send(chairs[i].addr, "lua", "leave_room")
			end
        end
    end
	
	if optionType == '1' then
		dealer:reset_game(host_id, zhengjing)
	else
		dealer:reset_game(host_id)
	end
	host_id_old = host_id
    --status = STATUS_FREE
    g_oper_card = nil
	pending_user = {}
	cur_id = nil
	last_out_card = {}
	has_cal_huitou = false
	log_data_id = nil
	gang_can_add_card	= 0
    --RESPOND.send_status(STATUS_FREE)
	
	if finish then 
		local roomid = room_id
		status = STATUS_FREE
		majiang_type = nil 
		gameType = 104 
		ruleType  = nil 
		total_ju = nil
		room_id = nil
		count = 0
		cur_ju = 1
		room_is_double = 1
		chairs = {} 
		dealer = nil
		
		-- 销毁房间
		skynet.call("MAJAING_HALL","lua","destory", roomid)
		skynet.exit()
	end
end

function send_data(id,data) 
    local rs = send_package(chairs[id].socket,data,room_id,chairs[id].uid)
    -- if rs == false then
    --     skynet.send( chairs[id].addr, "lua", "close" )
    --     skynet.send( chairs[id].addr, "lua", "kick" )
    -- end 
end

function send_other(id,data)
	time_out_stop_room = 0 --1个小时没动作自动解散房间
    for i=1,MAX_PLAYER_NUM do
        if i ~= id and chairs[i] then
            send_data(i,data)
        end
    end
end
function send_all(data) 
	if data.cmd == NetCmd.S2C_MJ_OUT_CARD then
		time_out_stop_room = 0 --1个小时没动作自动解散房间 
	end  
    for i=1,MAX_PLAYER_NUM do
        if chairs[i] then
            send_data(i,data)
        end
    end
end

function REQUEST.clean_data(id,data) 
	if status == STATUS_PLAY then--游戏状态
		return 
	end 
	chairs[id].player:set_flag(1)

    for i=1,MAX_PLAYER_NUM do
		if not chairs[i] or chairs[i].player:get_flag() ~= 1 then
			return
		end
	end 
	status = STATUS_FREE
	reset_game(dealer:get_host())  
end

function REQUEST.ready(id,data)
	
	if status == STATUS_PLAY then--游戏状态
		return 
	end
    local player = chairs[id].player
    if data.is_double == nil or data.is_double ~= 2 then
    	data.is_double = 1
    end
    local is_double = data.is_double 
	player:set_is_double(is_double)

	if player:is_ready() == true then 
	    local packet = {
	        cmd 	= 	NetCmd.S2C_MJ_READY,
	        id 		= 	id,
	        is_double	= 	player:get_is_double(),
	        is_piao	= 	player:get_is_piao(),
			score	= 	player:get_total_score(),
	    }
	    send_all(packet)
		return
	end
	
    player:set_ready(true)
    local packet = {
        cmd = NetCmd.S2C_MJ_READY,
        id = id,
		score	= 	player:get_total_score(),
		is_double	= 	is_double,
    }
    send_all(packet)

    for i=1,MAX_PLAYER_NUM do
		if not chairs[i] or not chairs[i].player:is_ready() then
			return
		end
	end
	
	reset_game(dealer:get_host())
	host_id_old = dealer:get_host()

	-- 发送牌局信息
	packet = {
		cmd = NetCmd.S2C_MJ_GAME_INFO,
		time_out_card = OUT_CARD_TIME,
		time_wait_card = WAIT_CARD_TIME,
	}
	send_all(packet)

	no_start	= 1
	is_one	= 1
	game_start()	
	if jiesan_timer_id then 
		del_skynet_timer(jiesan_timer_id)
		jiesan_timer_id = nil
	end
	
end

function REQUEST.out_card(id,data)
    if g_oper_card then
        local player = chairs[id].player
        if not player:has_hand_card( data.card_data ) then
            return
        end
        --如果手里有 暗杠 不让出牌 
        if player:get_is_angang() == 1 then
            return
        end
        g_oper_card(id, OPER_OUT_CARD, data.card_data)
    end
end

function REQUEST.peng(id,data)
    if g_oper_card then g_oper_card(id, OPER_PENG, data) end
end

function REQUEST.gang(id,data)
    if g_oper_card then g_oper_card(id, OPER_GANG, data) end
end

function REQUEST.chi_card(id,data)
    if g_oper_card then g_oper_card(id, OPER_CHI_CARD, data) end
end

function REQUEST.chi_hu(id,data)
    if g_oper_card then g_oper_card(id, OPER_CHI_HU) end
end

function REQUEST.hu(id,data)
    if g_oper_card then g_oper_card(id, OPER_HU) end
end

function REQUEST.can_chi_card(id,data)
	if g_oper_card then g_oper_card(id, OPER_CAN_CHI_CARD) end	
end

function REQUEST.pass(id,data)
    local player = chairs[id].player
    player:set_is_angang(0)
    if g_oper_card then g_oper_card(id, OPER_EMPTY) end
end

function REQUEST.room_chat(id,data)
    local packet = {
        cmd = NetCmd.S2C_ROOM_CHAT,
        id = id,
		msg_type = data.msg_type,
        msg = data.msg
    }
    send_all(packet)
end

function RESPOND.send_status(status)
    local packet = {
        cmd = NetCmd.S2C_MJ_GAME_STATUS,
        status = status,
    }
    send_all(packet)
end

function REQUEST.leave_room(id,data)  
    if not chairs[id]  then return end
	if status ~= STATUS_FREE then return end
    CMD.leave(nil,chairs[id].uid,1)
end

function REQUEST.jiesan_room(id, data)  
	REQUEST.leave_room(id,data) 
end
 
function REQUEST.apply_jiesan(id,data)
	local packet = {
		cmd = NetCmd.S2C_MJ_APPLY_JIESAN,
		index = id,
	}
	if not chairs[id] then return end
	local player = chairs[id].player
	local last_time = player:get_jiesan_time()
	local curr_time = os.time()
	-- if curr_time - last_time < 60 then 
	-- 	packet.errno = ErrNo.APPLY_JIESAN_TIME
	-- 	send_data(id,packet)
	-- 	return 
	-- end
	
	dealer:reset_jiesan(curr_time)
	dealer:add_jiesan(id)
	chairs[id].player:set_jiesan_time()
	send_all(packet)
	
	if jiesan_timer_id then 
		del_skynet_timer(jiesan_timer_id)
		jiesan_timer_id = nil
	end
	-- 60秒自动解散
	local jiesan_fun = function()
		CMD.jiesan()	
	end
	jiesan_timer_id = add_skynet_timer(300,jiesan_fun) 
end

function REQUEST.apply_jiesan_agree(id,data)
	local packet = {
		cmd = NetCmd.S2C_MJ_APPLY_JIESAN_AGREE,
		index = id,
	}
	if not chairs[id] then return end
	local jiesan_time = dealer:get_jiesan_time()
	-- local curr_time = os.time()
	-- if curr_time - jiesan_time > 300 then 
	-- 	packet.errno = 1
	-- 	send_data(id,packet)
	-- 	return 
	-- end
	
	packet.typ = data.typ
	send_all(packet)
	
	if data.typ == 0 then  -- 不同意
		if jiesan_timer_id then 
			del_skynet_timer(jiesan_timer_id)
			jiesan_timer_id = nil
		end
		dealer:reset_jiesan()
	else
		dealer:add_jiesan(id)
	end
	
	local jiesan_len = dealer:get_jiesan()
	-- for i=1,MAX_PLAYER_NUM do
 --        if chairs[i] and chairs[i].player:is_out_line() == true then
	-- 		jiesan_len = jiesan_len + 1
 --        end
 --    end
	if jiesan_len >= MAX_PLAYER_NUM - 1 then 
		if jiesan_timer_id then 
			del_skynet_timer(jiesan_timer_id)
			jiesan_timer_id = nil
		end
		dealer:reset_jiesan()
		CMD.jiesan(nil,chairs[id].socket)	
	end
end

function REQUEST.voice(id, data)  
    local packet = {
        cmd = NetCmd.C2S_VOICE, 
        chair_id = id,
        msg 	= data.msg, 
        typ 	= data.typ, 
        time 	= data.time, 
    }
    send_other(id,packet)
end

function REQUEST.gps_info(id, data)
	if data.longitude > 0 and data.latitude > 0 and chairs[id] then 
		chairs[id].player:set_gps(data.latitude,data.longitude,data.type)
	end
end

function REQUEST.gps_distance(uid, data)
	data.id = math.floor(data.id)
	local packet = { cmd = NetCmd.C2S_GPS_DISTANCE,distance={},userInfo={}}
    for i=1,MAX_PLAYER_NUM do
    	local from_latitude = 0
    	local from_longitude = 0
    	local from_type = 0
        if chairs[i]  then
			from_latitude,from_longitude,from_type = chairs[i].player:get_gps()
			packet.userInfo[i] = 0
			if from_latitude > 0 and from_longitude > 0 and from_type == 0 then 
				from_latitude,from_longitude = chairs[data.id].player:WGStoGCJ(from_latitude,from_longitude)
			end
			if from_latitude > 0 and from_longitude > 0  then
				packet.userInfo[i] = 1 
			end 
		else
			packet.userInfo[i] = 0
		end
		for k=i+1,MAX_PLAYER_NUM do
			local distance_now = -1
			if from_latitude > 0 and from_longitude > 0  then 
				if chairs[k] then
					local to_latitude,to_longitude,to_type = chairs[k].player:get_gps()
					if to_latitude > 0 and to_longitude > 0 and to_type == 0 then
						to_latitude,to_longitude = chairs[i].player:WGStoGCJ(to_latitude,to_longitude)
					end
					if from_latitude > 0 and from_longitude > 0 and to_latitude > 0 
						and to_longitude > 0 then
						distance_now = chairs[i].player:getDistance(from_latitude,from_longitude,to_latitude,to_longitude)
					end  
				end
			end 
			table.insert(packet.distance,distance_now)
		end 
	end
	send_data(uid,packet)
end


function REQUEST.tiren(id, data)
	if status == STATUS_PLAY then return end
	if not chairs[data.chair_id] or data.chair_id == 1 or id  ~= 1  then 
		return
	end
	
    -- skynet.error("room_id:",room_id,"id:",data.chair_id,"out")

    -- 发送其他玩家离开的消息
    local packet = {
        cmd = NetCmd.S2C_MJ_TIREN,
        chair_id = data.chair_id,
    }
    send_all(packet)

    count = count - 1
    skynet.send(chairs[data.chair_id].addr, "lua", "leave_room")
 	chairs[data.chair_id] = nil
	skynet.call("YLMJ_HALL","lua","room_status", room_id, { count = count })
end


REQUEST.func_map = {
    [ NetCmd.C2S_MJ_READY ]            		= REQUEST.ready,
    [ NetCmd.C2S_MJ_LEAVE_ROOM ]       		= REQUEST.leave_room,
	[ NetCmd.C2S_MJ_JIESAN ]	       		= REQUEST.jiesan_room,
	[ NetCmd.C2S_MJ_APPLY_JIESAN ]			= REQUEST.apply_jiesan,
	[ NetCmd.C2S_MJ_APPLY_JIESAN_AGREE ]	= REQUEST.apply_jiesan_agree,
	
    [ NetCmd.C2S_MJ_OUT_CARD ]         		= REQUEST.out_card,
    [ NetCmd.C2S_MJ_PENG ]             		= REQUEST.peng,
    [ NetCmd.C2S_MJ_GANG ]             		= REQUEST.gang,
    [ NetCmd.C2S_MJ_CHI_CARD ]         		= REQUEST.chi_card,
	[ NetCmd.C2S_MJ_CAN_CHI_CARD ]     		= REQUEST.can_chi_card,
    [ NetCmd.C2S_MJ_CHI_HU ]           		= REQUEST.chi_hu,
    [ NetCmd.C2S_MJ_HU ]               		= REQUEST.hu,
	[ NetCmd.C2S_PASS ]               		= REQUEST.pass,
    [ NetCmd.C2S_ROOM_CHAT ]           		= REQUEST.room_chat,
	[ NetCmd.C2S_MJ_TIREN ]					= REQUEST.tiren,
	[ NetCmd.TEST ]							= REQUEST.test,
	
	[ NetCmd.S2C_VOICE ]					= REQUEST.voice,
	[ NetCmd.S2C_GPS_INFO ]					= REQUEST.gps_info,
	[ NetCmd.S2C_GPS_DISTANCE ]				= REQUEST.gps_distance,
	[ NetCmd.C2S_CLEAN_DATA ]				= REQUEST.clean_data,
}

function find_id(address)
    for i=1,MAX_PLAYER_NUM do
        if chairs[i] and address == chairs[i].addr then
            return i,chairs[i].uid
        end
    end
    return nil,nil
end


function get_all_cards(packet)
	-- 显示全部人的牌	
	packet.players = {}
	local hu_id = dealer:get_hu_id()
	local dibei = 1
	local is_pao_deiguo = 0
	if hu_id and hu_id ~= 0 then
		local hu_player = chairs[hu_id].player
		if hu_player:is_zimo() == false then 
			for i,v in ipairs(hu_player:get_hu_types()) do 
				if v == MJLogic.HU_DEIGUO or v == MJLogic.HU_DEIZHONGDEI then 
					is_pao_deiguo = v
				end
			end
		end
		dibei = dealer:get_dibei(3)
	end
	for i=1,MAX_PLAYER_NUM do
		local player = chairs[i].player
		packet.players[i] 				= {}
		packet.players[i].chair_id 		=  i
		packet.players[i].uid			= chairs[i].uid
		packet.players[i].name			= chairs[i].name 
		packet.players[i].head			= chairs[i].head 
		packet.players[i].sex 			= chairs[i].sex
		packet.players[i].groups 		= player:get_group()
		packet.players[i].hands 		= player:get_hand()
		packet.players[i].hu_cards 		= player:get_hu_cards()
		packet.players[i].hu_types 		= player:get_hu_types()
		
		packet.players[i].score 		= player:get_score()	
		packet.players[i].allScore  	= player:get_score()	
		packet.players[i].total_score 	= player:get_total_score()	
		packet.players[i].is_dianpao	= player:is_dianpao()
		packet.players[i].is_jiepao		= player:get_is_jiepao()
		packet.players[i].is_zimo 		= player:is_zimo()
		packet.players[i].minggang 		= player:get_gang_score()
		packet.players[i].angang 		= 0--player:get_angang() * 2
		if i ~= hu_id then 
			packet.players[i].dibei			= dibei
			packet.players[i].is_dianpao 	= player:is_dianpao()
			packet.players[i].is_pao_deiguo	= is_pao_deiguo
		end
		packet.players[i].jingfen	= player:get_jingfen(3)
		packet.players[i].xiajing_fen	= player:get_jingfen(1)
		packet.players[i].huitou_fen	= player:get_jingfen(2)
	end	
end

function get_last_result(packet)
	packet.results = {}
	for i=1,MAX_PLAYER_NUM do
		local player = chairs[i].player
		packet.results[i] = {}
		packet.results[i].id = i
		packet.results[i].stat = player:get_stat()
		packet.results[i].total_score = player:get_total_score()
	end	
end

function CMD.get_users()
	local ret = {}
	for i=1,MAX_PLAYER_NUM do 
		if chairs[i] then 
			ret[i] 			= {}
			ret[i].index 	= 	i
			ret[i].uid		=	chairs[i].uid
			ret[i].name		=	chairs[i].name 
			ret[i].head		=	chairs[i].head 
			ret[i].sex 		=	chairs[i].sex
		end
	end
	local room_info = {}
	room_info.gameType = 104
	room_info.ruleType = ruleType
	room_info.optionType = optionType
	return ret,room_info
end

function CMD.join(address,client_fd,uid,name,head,sex,ip)
    if count >= MAX_PLAYER_NUM then
        return { index = 0, count = count }
    end
	
	local arr_no_id = {}
	--先查找是否在房间
	for i=1,MAX_PLAYER_NUM do
		if chairs[i] and chairs[i].uid == uid then
 			return { index = 0, count = count }
		end
        if not chairs[i] then
        	table.insert(arr_no_id, i)
		end
	end
 
    count = count + 1
	math.randomseed( tonumber(tostring(os.time()):reverse():sub(1,6)) )
	local rand = math.random(1,#arr_no_id);
	local id =  arr_no_id[rand] --随机id 
	if  #arr_no_id == MAX_PLAYER_NUM then
		id = 1  --房主为 1
	end

    chairs[id] = { socket=client_fd, addr = address, uid = uid, name = name, head = head, sex = sex, ip = ip, player = Player.new(true) }
 
    -- 发送新加入者牌局信息
    local others = {}
    local user_info = {}
    for i=1,MAX_PLAYER_NUM do
        if chairs[i] then
            local other = { 
				chair_id=   i, 
				uid		=	chairs[i].uid, 
				name	=	chairs[i].name, 
				head	=	chairs[i].head, 
				sex 	=	chairs[i].sex,
				ip		=	chairs[i].ip, 
				ready	=	chairs[i].player:is_ready(),
				is_double	= 	chairs[i].player:get_is_double(),
				is_piao	= 	chairs[i].player:get_is_piao(),
				score	= 	chairs[i].player:get_total_score(),
			}
	        if i ~= id then
	            table.insert(others,other)
	        end
	        table.insert(user_info,other)
        end
    end
    local packet = {
        cmd = NetCmd.S2C_MJ_TABLE_USER_INFO,
        user_info 		= user_info, 
    }
    send_other(id,packet)

    return { 
		chair_id = id, 
		index	= id,
		status = status, 
		count = count, 
		majiang_type = majiang_type,
		room_is_double = room_is_double, 
		ruleType 	= ruleType, 
		gameType = 104, 
		optionType 	= optionType,
		room_type = room_type,
		total_ju = total_ju,
		dizhu = dizhu,
		host_id = dealer:get_host(),
		other = others,
		player_info = {
			chair_id	=	id, 
			uid		= uid,
			name 	= name,
			head 	= head,
			ip 		= ip,
			sex		= sex,
			is_double	= 	chairs[id].player:get_is_double(),
			ready	=	chairs[id].player:is_ready(),
			score	= 	chairs[id].player:get_total_score(),
		}, 
	}
end


function CMD.join_again(address,client_fd,uid,name,head,sex,ip)
	
	local index = -1
	for i=1,MAX_PLAYER_NUM do
		if chairs[i] and chairs[i].uid == uid then
			chairs[i].socket = client_fd
			chairs[i].addr = address
			chairs[i].uid = uid
			chairs[i].name = name
			chairs[i].head = head
			chairs[i].sex = sex
			chairs[i].ip = ip
 			index = i
			break;
		end	
	end
	if index > 0 then
		-- 查询其他玩家的信息
		local others = {}
		for i=1,MAX_PLAYER_NUM do
			if chairs[i] and i ~= index then
				local player = chairs[i].player
				local other = { 
					chair_id	=	i, 
					uid			=	chairs[i].uid, 
					name		=	chairs[i].name, 
					head		=	chairs[i].head, 
					sex			= 	chairs[i].sex,	
					ip			=	chairs[i].ip,   
					ready		=	player:is_ready(),
					out_line 	= 	player:is_out_line(),
					score		= 	player:get_total_score(),
					has_hand_card_num	= 	player:get_hand_count(),
				}
				
				-- 手上的牌
				other.group_card = player:get_group()
				other.out_card = player:get_out_cards()
				
				table.insert(others,other)
			end
		end	
		local self_player = chairs[index].player
		
		local actions = {}
		if #pending_user > 0 then
			for i=#pending_user,1,-1 do	
				if pending_user[i].id == index then
					table.insert(actions,pending_user[i].oper)	
				end
			end	
		end
		
		local inline_packet = {
			cmd  = NetCmd.S2C_MJ_IN_LINE,
			index = index,
		}
		send_other(index, inline_packet)
		self_player:set_out_line(false)
		
		
		local info =  { 
			chair_id 	= index, 
			status 		= status, 
			count 		= count, 
			majiang_type= majiang_type, 
			ruleType 	= ruleType, 
			gameType = 104,
			optionType 	= optionType, 
			room_is_double = room_is_double,
			room_type 	= room_type,
			total_ju 	= total_ju,
			cur_ju		= cur_ju,
			dizhu 		= dizhu,
			left_card_num	= dealer:get_rest_cards_count(),
			host_id = dealer:get_host(),
			zhengjing = zhengjing,
			fujing = fujing,
			xia_zhengjing	= xia_zhengjing,
			xia_fujing 		= xia_fujing,
			cur_id = cur_id,
			actions 	= 	actions,
			player_info = {
				chair_id	=	index, 
				uid			=	chairs[index].uid, 
				name		=	chairs[index].name, 
				head		= 	chairs[index].head, 
				sex			=	chairs[index].sex, 
				ip			=	chairs[index].ip,  
				is_double	= 	chairs[index].player:get_is_double(),
				ready		=	self_player:is_ready(),
				score		= 	self_player:get_total_score(),
				last_card	= 	self_player:get_last_card(),
				hand_card  = {}
			},
			hand_card 	= self_player:get_hand(), 
			group_card 	= self_player:get_group(), 
			out_card	= self_player:get_out_cards(), 
			other 		= others, 
		}
		
		if status == STATUS_RESULT and info.player_info.ready == false then 
			local packet = {
				cmd 		= NetCmd.S2C_RESULT,
				total_ju 	= total_ju, 
				room_is_double = room_is_double,
				cur_ju 		= cur_ju-1,
				status 		= status,
				hu_users 	= dealer:get_hu_users(),
				--is_jiesan 	= true,
			}
			get_all_cards(packet)
			if cur_ju > total_ju then
				get_last_result(packet)
			end
			info.result_packet = packet
		end
		return info
	else
		return nil
	end
end

function CMD.out_line(address,uid)
	local id = -1
	for i=1,MAX_PLAYER_NUM do
		if chairs[i] and chairs[i].uid == uid then
			id = i
			break
		end
	end	
	if id > 0 then
		local packet = {
			cmd = NetCmd.S2C_MJ_OUT_LINE,
			index = id,
		}
		send_other(id,packet)	
		chairs[id].player:set_out_line(true)
	end
	return
end
function CMD.in_line(address, uid)
	local id = -1
	for i=1,MAX_PLAYER_NUM do
		if chairs[i] and chairs[i].uid == uid then
			id = i
			break
		end
	end	
	if id > 0 then
		local packet = {
			cmd 	= NetCmd.S2C_MJ_IN_LINE,
			index 	= id,
		}
		send_other(id,packet)	
		chairs[id].player:set_out_line(false)
	end
	return
end

function CMD.leave(address,leave_uid,flag)
	if flag == nil and no_start == 1 then
		return 99
	end
	if status == STATUS_PLAY then
		return 99
	end
		
	local id = 0
	local count_now = 0
    for i=1,MAX_PLAYER_NUM do
        if chairs[i] and chairs[i].uid == leave_uid then
            id = i 
        end
        if chairs[i] and chairs[i].uid ~= leave_uid then
            count_now = 1
        end
    end
    if id == 0 then return 0 end
 
	-- 发送其他玩家离开的消息
    local packet = {
        cmd = NetCmd.S2C_MJ_LEAVE_ROOM,
        id = id,
    }
    send_all(packet)

    count = count - 1 
    skynet.send(chairs[id].addr, "lua", "leave_room")
    chairs[id] = nil
    skynet.call("MAJAING_HALL","lua","room_status", room_id, { count = count })
	if count_now == 0 then --没人了 解散房间
		CMD.jiesan(nil,nil)
	end
	return 0
end

function CMD.jiesan(address,fd)
	if is_one == 1 then  
		dealer:clean_log_data() 
		local logs = {}
	    for i=1,MAX_PLAYER_NUM do
	        if chairs[i] then
	            local player = chairs[i].player   
	            table.insert(logs, {
						chair_id 	= i,
						uid 		= chairs[i].uid,
						name 		= chairs[i].name,
						head 		= chairs[i].head,
						sex 		= chairs[i].sex,
						ip 			= chairs[i].ip,
						is_double 		= player:get_is_double(),
						is_piao 		= player:get_is_piao(), 
				}) 
	        end
	    end 
		dealer:set_log_players(logs) 
		local room_info = {
			room_id			= room_id,
			total_ju 		= total_ju,
			majiang_type	= majiang_type,
			hu_type 		= hu_type,
			ruleType 		= ruleType,
			gameType = 104,
			optionType 		= optionType,
			cur_ju			= cur_ju,
			room_is_double			= room_is_double,
			room_is_piao			= room_is_piao,
			host_id 		= dealer:get_host(),
		}
		dealer:set_log_room_info(room_info)
		game_result(nil, true) 
	end
	local packet = {
		cmd = NetCmd.S2C_MJ_JIESAN,
	}
	send_all(packet)	
	
	-- 全部人离开房间
	 for i=1,MAX_PLAYER_NUM do
		 if chairs[i] then
			 skynet.send(chairs[i].addr, "lua", "leave_room")	
		 end
	 end
		 
	-- 销毁房间
	skynet.call("MAJAING_HALL","lua","destory", room_id)	
	skynet.exit() 
end

--room_id, data.game_type, data.rule_type, data.dizhu, data.room_type, data.total_ju, data.friend_id)
function CMD.start(address,id,gtype,opte ,dbei,rmtype,tju,fid,room_is_d)
	room_id = id	
	ruleType = math.ceil(gtype)
	optionType = opte
	room_type = math.ceil(rmtype)
	dizhu = math.ceil(dbei)
    total_ju = math.ceil(tju)
	friend_id = fid
	room_is_double = room_is_d

	local time_out_stop_room_fun = function()       -- 定时器触发 
		time_out_stop_room = time_out_stop_room + 1 
		if time_out_stop_room >= 72 then --3600 秒后 没动作，自动解散房间
			CMD.jiesan(nil,nil)
		end
	end
	add_skynet_timer2( 100,time_out_stop_room_fun)
end

skynet.register_protocol {
	name = "client",
	id = skynet.PTYPE_CLIENT,
    unpack = function(msg,sz)
        return skynet.tostring(msg,sz)
    end,
	dispatch = function (_, address, text, ...)
        local id,uid = find_id(address)
        if not id then
            -- skynet.error("invalid address",address)
            return
        end
        local json_data = unpack_package(text,room_id,uid)
        if not json_data then
			if not json_data then skynet.error "invalid json data" end
            return
        end
        local f = REQUEST.func_map[json_data.cmd]
        if not f then
            -- skynet.error(string.format("room invalid json cmd:%d, id:%d", json_data.cmd, id))
            return
        end
        f(id,json_data)
    end
}

skynet.start(function()
	skynet.dispatch("lua", function(_,address, command, ...)
		local f = CMD[command]
		skynet.ret(skynet.pack(f(address,...)))
	end)
end)

function next_id(id)
	return id == MAX_PLAYER_NUM and 1 or id + 1
end
function prev_id(id)
	return id == 1 and MAX_PLAYER_NUM or id - 1
end
function priority_id(id,last_id)
	return id > last_id and id - last_id or id + MAX_PLAYER_NUM - last_id
end


function game_start()
    status = STATUS_PLAY

    local start_id = dealer:get_host()
    local packet = {
        cmd 			= NetCmd.S2C_MJ_GAME_START,
        status 			= STATUS_PLAY,
        cards 			= nil,
        cur_user 		= start_id,
        chair_id 		= start_id,
		cur_ju 			= cur_ju,
		room_is_double			= room_is_double,
		host_id 		= dealer:get_host(),
    }
	
	dealer:shuffle()
	local jing_card = dealer:draw_card(dealer:get_rest_cards_count(), 24)
	zhengjing = jing_card
	fujing = MJLogic.NextCard(jing_card)
	
	packet.zhengjing = zhengjing
	packet.fujing = fujing
	packet.shaizi_jing = MJLogic.Shaizi()
	packet.left_card_num = dealer:get_rest_cards_count()
	start_shaizi = packet.shaizi_jing[1] + packet.shaizi_jing[2]
	local logs = {}
    for i=1,MAX_PLAYER_NUM do
        if chairs[i] then			
            local player = chairs[i].player
            player:insert_hand_card( dealer:get_initial_card(i) )
            packet.cards = player:get_hand()
			local tmp_cards = {}
			for i,v in ipairs(player:get_hand()) do
				table.insert(tmp_cards, v)
			end
            table.insert(logs, {
					chair_id 	= i,
					uid 		= chairs[i].uid,
					name 		= chairs[i].name,
					head 		= chairs[i].head,
					sex 		= chairs[i].sex,
					cards		= tmp_cards,
					ip		=	chairs[i].ip, 
			})
			send_data(i,packet)
        end
    end
	dealer:add_log_order(packet)
	dealer:set_log_players(logs)
	
	local room_info = {
		room_id			= room_id,
		total_ju 		= total_ju,
		majiang_type= majiang_type, 
		ruleType 	= ruleType,
		gameType = 104, 
		optionType 	= optionType,
		room_type 	= room_type,
		dizhu 		= dizhu,
		zhengjing		= zhengjing,
		fujing			= fujing,
		xia_zhengjing	= xia_zhengjing,
		xia_fujing 		= xia_fujing,
		huitou_zhengjing = dealer:get_houtou_zhengjing(),
		cur_ju			= cur_ju,
		room_is_double			= room_is_double,
		host_id 		= dealer:get_host(),
	}
	dealer:set_log_room_info(room_info)
	
    out_card_procedure(start_id)
end

function send_cool_down(id, time, pending_user)
   
	local packet = {
        cmd = NetCmd.S2C_MJ_COOL_DOWN,
        recv_id = id,
        time = time,
    }
	if #pending_user > 0 then
		for j=1,MAX_PLAYER_NUM do 
			local actions = {}
			for i=#pending_user,1,-1 do	
				if pending_user[i].id == j then
					table.insert(actions,pending_user[i].oper)	
				end
			end	
			--if j == id and time == OUT_CARD_TIME then
			--	table.insert(actions,OPER_OUT_CARD)
			--end
			packet.index = j
			packet.actions = actions
			send_data(j,packet)
			if #actions > 0 then 
				local tmp  = {
					cmd 	= NetCmd.S2C_MJ_COOL_DOWN,
					recv_id = id,
					time 	= time,
					index 	= j,
					actions	= actions,
				}
				dealer:add_log_order(tmp)
			end
		end
	else
		send_all(packet)	
	end
end

function send_out_card(id,card)
    local packet = {
        cmd = NetCmd.S2C_MJ_OUT_CARD,
        out_card_user = id,
        out_card_data = card,
    }
    send_all(packet)
	packet.chair_id = id
	dealer:add_log_order(packet)
end

function send_draw_card(id,data)
    local packet = {
        cmd = NetCmd.S2C_MJ_DRAW_CARD,
        cur_user = id,
        chair_id = id,
		left_card_num = dealer:get_rest_cards_count(),
    }
    send_other(id, packet)
    packet.draw_card_data = data
    send_data(id, packet)
	packet.chair_id = id
	dealer:add_log_order(packet)
end

function send_peng(id,card)
    local packet = {
        cmd = NetCmd.S2C_MJ_PENG,
        cur_user = id,
        chair_id = id,
        cards = card,
    }
    send_all(packet)
	packet.chair_id = id
	dealer:add_log_order(packet)
end


function send_gang(id,card,typ)
    local packet = {
        cmd = NetCmd.S2C_MJ_GANG,
        cur_user = id,
        chair_id = id,
        cards = card,
    }
	packet.typ = typ
    send_all(packet)
	packet.chair_id = id
	dealer:add_log_order(packet)
end

function send_chi_card(id,card,group)
    local packet = {
        cmd = NetCmd.S2C_MJ_CHI_CARD,
        cur_user = id,
        chair_id = id,
        cards = card,
        group = group,
    }
    send_all(packet)
	packet.chair_id = id
	dealer:add_log_order(packet)
end

function send_can_chi_card(id,arr) 
	local packet = {
		cmd = NetCmd.S2C_MJ_CAN_CHI_CARD,
		cur_user = id,
		chair_id = id,
		arr = arr,
	}
	send_data(id, packet)
end

function send_chi_hu(id,card)
    local packet = {
        cmd = NetCmd.S2C_MJ_CHI_HU,
        cur_user = id,
        chair_id = id,
        hu_card = card,
		cur_ju	= cur_ju,
    }
	get_all_cards(packet)
    send_all(packet)
	packet.chair_id = id
	dealer:add_log_order(packet)
end

function send_hu(id,card)
    local packet = {
        cmd = NetCmd.S2C_MJ_HU,
        cur_user = id,
        chair_id = id,
        hu_card = card,
		cur_ju	= cur_ju,
    }
	get_all_cards(packet)
    send_all(packet)
	packet.chair_id = id
	dealer:add_log_order(packet)
end

function add_animat(wait_time, oper)
	table.insert(animats, {wait_time,oper})
end

function calcu_jingfen(id, card, fucard)
	local jing_fen = 0
	for j,v in ipairs(chairs[id].player:get_hand()) do
		if v == card then 
			jing_fen = jing_fen + 2
		elseif v == fucard then
			jing_fen = jing_fen + 1
		end
	end
	
	for j,v in ipairs(chairs[id].player:get_group()) do
		for k,g in ipairs(v) do 
			if k > 4 then break end
			if g == card then 
				jing_fen = jing_fen + 2
			elseif g == fucard then
				jing_fen = jing_fen + 1
			end
		end
	end
	
	for j,v in ipairs(chairs[id].player:get_out_cards()) do 
		if v == card then 
			jing_fen = jing_fen + 2
		elseif v == fucard then
			jing_fen = jing_fen + 1
		end
	end
	
	if jing_fen >= 5 then
		jing_fen = jing_fen * (jing_fen - 3)
	end
	return jing_fen
end

function do_calcu_jingfen(card, fucard, typ)
	local users = {}
	for i=1,MAX_PLAYER_NUM do 
		users[i] = calcu_jingfen(i,card,fucard)
	end
	
	for j=1,MAX_PLAYER_NUM do 
		local tmp = 0
		for i=1,MAX_PLAYER_NUM do 
			if i ~= j then tmp = tmp + users[i] end	
		end
		local base_fen = tmp == 0 and users[j] * 2 or users[j]
		if base_fen > 0 then
			for k=1,MAX_PLAYER_NUM do 
				if k ~= j then
					chairs[k].player:decr_jingfen(typ, base_fen)
				end
			end
			chairs[j].player:add_jingfen(typ, base_fen * (MAX_PLAYER_NUM - 1))
		end
	end	
end

local ORDER_OPER = { [OPER_HU]= 11, [OPER_CHI_HU]=10, [OPER_GANG]=9, [OPER_PENG]=8, [OPER_CHI_CARD]=7, }
function find_order_index(pending_user,id,oper)
    local order = -1
    for i,action in ipairs(pending_user) do
        if ORDER_OPER[oper] < ORDER_OPER[action.oper] then
            return i
        end
    end
    return order
end


function add_pending_user( pending_user, id, oper, card)
    local action = {id=id, oper=oper,data=nil,done=false,card=card}
    local order = find_order_index(pending_user,id,oper)
    if order <= 0 then
        table.insert(pending_user, action)
    else
        table.insert(pending_user, order, action)
    end
end


function do_pending_user( pending_user, id, oper, data)
    local cid = -1
    for i,action in ipairs(pending_user) do
        if action.done == false and action.id == id and action.oper == oper then
            action.data = data
			cid = action.id
            break
        end
    end
	if pending_user[#pending_user].oper ~= oper and cid == pending_user[#pending_user].id then 
		table.remove(pending_user,#pending_user)
		return do_pending_user( pending_user, id, oper, data)
	else
		return cid == pending_user[#pending_user].id and true or false
	end
	--return cid == pending_user[#pending_user].id and true or false
end

function find_pending_user_hu_card(pending_user, id, oper)
	for i,action in ipairs(pending_user) do
		if action.id == id and action.oper == oper then 
			if action.card then return action.card end
		end
	end
	return nil
end

function do_hu_pending_user( pending_user, id, oper, last_user)
	local first_id = 0
	local min_dis = MAX_PLAYER_NUM
	for i,action in ipairs(pending_user) do
        if  action.oper == oper then
			local tmp = priority_id(action.id, last_user)
			if tmp < min_dis then
				min_dis = tmp
				first_id = action.id
			end
        end
    end
	return first_id == id and true or false
end

function is_bawang(id)
	for i=1,MAX_PLAYER_NUM do
		if i ~= id then 
			for j,v in ipairs(chairs[i].player:get_hand()) do 
				if v == zhengjing or v == fujing then
					return false
				end
			end
		end
	end
	return true
end

local PERIOD_ACTION     = 1
local PERIOD_PENDING    = 2
local PERIOD_ANIMAT		= 3
function out_card_procedure(start_id)
	for i=1,MAX_PLAYER_NUM do
        if chairs[i] then
            MJLogic.sortASCE(chairs[i].player:get_hand())
        end
    end
	

    last_out_card = {}
    pending_user = {}
	
    local last_user = start_id
    cur_id = start_id
    local timer_cb
    local timer_id
    local period = PERIOD_ACTION
    local wait_time = OUT_CARD_TIME

	if ruleType == 0 then
		period = PERIOD_ANIMAT
		wait_time = 1
		add_animat(1,OPER_JINGFEN)
	end
	
	if ruleType == 1 and optionType == '1' then 
		period = PERIOD_ANIMAT
		wait_time = 1
		add_animat(1,OPER_HUITOU)
	end
		
    local function do_draw_card( id )
		if jiesan_timer_id then 
			del_skynet_timer(jiesan_timer_id)
			jiesan_timer_id = nil
		end
        dealer:set_tiandi_hu(false)
		cur_id = id            
        period = PERIOD_ACTION
        wait_time = OUT_CARD_TIME

        local player = chairs[cur_id].player 
		local need_left_num = start_shaizi * 2

		if gang_can_add_card == 1 then
			need_left_num = need_left_num - 2
			gang_can_add_card = 2
		end
        local card = dealer:draw_card(dealer:get_rest_cards_count(), need_left_num)
        if not card then
			do_calcu_jingfen(zhengjing, fujing, 3)
			if ruleType == 1 then
				period = PERIOD_ANIMAT
				wait_time = 1
				add_animat(1,OPER_JINGFEN)
				add_animat(1,OPER_RESULT)
			else
				game_result()
			end
            return
        end
        player:insert_hand_card( {card} )
		player:clean_cant_peng_card()
		local hand = player:get_hand()
		local group = player:get_group()
		local gang_card,is_angang = MJLogic.GetCanGang(hand,group, player:get_cant_gang_card())
		if gang_card then
			if is_angang == 1 or is_angang == 3 then 
				if is_angang == 3 then --公杠没杠，后面不能杠
					player:add_cant_gang_card(gang_card)
				end
				player:set_is_angang(1) --手里有暗杠
			end
			add_pending_user(pending_user, id, OPER_GANG)
		end
		if MJLogic.CanZimoHu( hand, group, zhengjing, fujing) == true then
			add_pending_user(pending_user, id, OPER_HU)
		end
        send_draw_card(cur_id, {card})
    end
    local function do_oper_peng(id,data)
		dealer:set_tiandi_hu(false)
        local player = chairs[id].player
        player:clean_cant_peng_card()
        dealer:reset_out_card()
		if player:has_hand_card_num(last_out_card[1]) == 3 then 
			player:add_cant_gang_card(last_out_card[1])
		end
        player:do_peng(last_out_card)
		chairs[last_user].player:remove_out_card()	
        send_peng(id,last_out_card)    
        cur_id = id                  
        period = PERIOD_ACTION
        wait_time = OUT_CARD_TIME
        pending_user = {}
    end
    local function do_oper_gang(id,data,typ)
		if typ ~= 2 then 
			dealer:set_tiandi_hu(false)
		end
        local player = chairs[id].player
        player:clean_cant_peng_card()
        dealer:reset_out_card()
        player:do_gang(data,typ,last_user)
		if typ == 1 then
			chairs[last_user].player:remove_out_card()	
			chairs[last_user].player:add_hu_type(MJLogic.HU_GANG_FANG)	
		end
		send_gang(id,data,typ)
		if typ == 3 then
			dealer:set_qianggang(true)
		end
		dealer:set_gangkai(true)
		dealer:add_gang_num()
        pending_user = {}
		
		for i=1,MAX_PLAYER_NUM do
			local player = chairs[i].player
			if i == id then 
				player:add_jingfen(4,6)
			else
				player:decr_jingfen(4,2)
			end
		end
		
		local can_hu = false
		if typ == 3 then 
			for i=1,MAX_PLAYER_NUM do 
				if i ~= id then
					local other_player = chairs[i].player
					if player:get_louhu_score() == false and MJLogic.CanChiHu(other_player:get_hand(), other_player:get_group(), data[1], zhengjing, fujing) == true then
						can_hu = true
						add_pending_user(pending_user, i, OPER_CHI_HU)
					end
				end
			end
		end
		
		if can_hu == true then
			last_user = id
			cur_id = prev_id(id)
			last_out_card = {data[1]}
			period = PERIOD_PENDING
			wait_time = WAIT_CARD_TIME
		else
			
			do_draw_card(id)	
		end
    end
    local function do_oper_chi_card(id,data)
		dealer:set_tiandi_hu(false)
        local player = chairs[id].player
        dealer:reset_out_card()
		MJLogic.sortASCE(data)
        player:do_chi_card(last_out_card, data)
		chairs[last_user].player:remove_out_card()
        send_chi_card(id,last_out_card,data) -- 发送吃牌消息
        cur_id = id                     -- 指定下一轮玩家
        period = PERIOD_ACTION
        wait_time = OUT_CARD_TIME
        pending_user = {}
    end

    function get_pending_access()
        return #pending_user > 0
    end

    local function oper_out_card(id,data)
		if cur_id ~= id then return end
		if #pending_user > 0 then 
			for i=#pending_user,1,-1 do	
				if pending_user[i].oper ~= OPER_GANG  then
					local packet = {
						cmd 		= NetCmd.S2C_MJ_OUT_CARD,
						index 		= id,
						errno 		= ErrNo.CANT_OUT_CARD,
					}
					send_data(id, packet)			
					return 
				end
			end	
		end
		
        if not data   then
	    	return 0 
	    end
	    if chairs[id].player:can_out_card() == false then 
	    	return 0 
	    end 	
        chairs[id].player:delete_hand_card(data)
		if chairs[id].player:out_card_isok() == false then 
			chairs[id].player:insert_hand_card(data)
			return
		end
        last_out_card = data	
        last_user = id			
        dealer:out_card(data[1])
		chairs[id].player:insert_out_card(data)
        send_out_card(id,data)
        MJLogic.sortASCE(chairs[id].player:get_hand())


        local zhui_zhuang_num = dealer:get_is_zhui_zhuang()
		if zhui_zhuang_num < 3 then
			if dealer:get_is_zhui_zhuang_card() == 0 then
				dealer:set_is_zhui_zhuang_card(data[1])
			elseif dealer:get_is_zhui_zhuang_card() == data[1] then
				zhui_zhuang_num = zhui_zhuang_num + 1
				dealer:set_is_zhui_zhuang(zhui_zhuang_num)
			else
				dealer:set_is_zhui_zhuang(99)
			end
		end


		pending_user = {}
		chairs[id].player:set_louhu_score(false)
		dealer:set_gangkai(false)
		dealer:set_qianggang(false)
		
        local can_hu, can_peng, can_gang = false,false,false
        for i=1,MAX_PLAYER_NUM do
            if chairs[i] and id ~= i then
                local player = chairs[i].player
                if player:get_louhu_score() == false and MJLogic.CanChiHu(player:get_hand(), player:get_group(), data[1], zhengjing, fujing) == true then
                    can_hu = true
                    add_pending_user(pending_user, i, OPER_CHI_HU)
                end
                if MJLogic.CanPeng(player:get_hand(), player:get_cant_peng_card(), data[1]) == true then
                    can_peng = true
                    add_pending_user(pending_user, i, OPER_PENG)
                end
                if MJLogic.CanGang(player:get_hand(), data[1]) == true then
                    can_gang = true
                    add_pending_user(pending_user, i, OPER_GANG)
                end
            end
        end

        local can_chi_pai = false
		local nxt_id = next_id(id)
		if chairs[nxt_id] then
			local player = chairs[nxt_id].player
			local ret = {}
			if MJLogic.CanChi(player:get_hand(), data[1], ret) == true then
				can_chi_pai = true
				add_pending_user(pending_user, nxt_id, OPER_CHI_CARD)
			end
		end
        
		
        if can_hu == true or can_peng == true or can_gang == true or can_chi_pai == true then
            period = PERIOD_PENDING
            wait_time = WAIT_CARD_TIME
            return 1
        end
		
        do_draw_card(next_id(id))
        return 1
    end
    local function oper_peng(id,data)
        if chairs[id] then
            local player = chairs[id].player
            if MJLogic.CanPeng(player:get_hand(), player:get_cant_peng_card(), last_out_card[1]) == false then
                return 0
            end

            local done = do_pending_user(pending_user, id, OPER_PENG, last_out_card)
            if not done then
                return 0
            end
			if id == cur_id then return end
	    	if dealer:get_is_zhui_zhuang()  < 3 then
		        dealer:set_is_zhui_zhuang(99)
		    end
			cur_id = id
            do_oper_peng(id,last_out_card)
            return 1
        end
        return 0
    end
    local function oper_gang(id,data)
        if chairs[id] then 
			local done = do_pending_user(pending_user, id, OPER_GANG, last_out_card)
            if not done then
                return 0
            end
	    	if dealer:get_is_zhui_zhuang()  < 3 then
		        dealer:set_is_zhui_zhuang(99)
		    end
            local player = chairs[id].player
            if period == PERIOD_ACTION then
                local gang_data = MJLogic.GetGangPai(player:get_hand())
                if #gang_data > 0 then
                	player:set_is_angang(0)
                    do_oper_gang(id,{gang_data[1]},2)
					return 1
                end
				local gang_card = MJLogic.GetGroupGangpai(player:get_hand(),player:get_group())
				if gang_card > 0 then
                	player:set_is_angang(0)
					do_oper_gang(id,{gang_card},3)
					return 1
				end
				return 0
            elseif period == PERIOD_PENDING then
                if MJLogic.CanGang(player:get_hand(), last_out_card[1]) == false then
                    return 0
                end
                local done = do_pending_user(pending_user, id, OPER_GANG, last_out_card)
                if not done then
                    return 0
                end
                do_oper_gang(id,last_out_card,1)
                return 1
            end
            return 0
        end
        return 0
    end
    local function oper_chi_card(id,data)
        if chairs[id] then
            local cards_ret = {}
            local player = chairs[id].player
            if id ~= next_id(last_user) or MJLogic.CanChi(player:get_hand(), last_out_card[1], cards_ret) == false then
                return 0
            end

            local do_data = nil
            if data.card_data and #cards_ret > 1 then
				local do_card_data = {data.card_data[1],last_out_card[1],data.card_data[2]}
                for i,group in ipairs(cards_ret) do
                    if not do_data and MJLogic.has_card(group, do_card_data) == true then
                        do_data = do_card_data
						break
                    end
                end
            else
                do_data = cards_ret[1]
            end
            if not do_data then
                return 0
            end

            local done = do_pending_user(pending_user, id, OPER_CHI_CARD, do_data)
            if not done then
                return 0
            end
			if id == cur_id then return end				
			cur_id = id 
            do_oper_chi_card(id,{do_data[1], last_out_card[1], do_data[3]})
            return 1
        end
        return 0
    end
    local function oper_chi_hu(id,data)
        if chairs[id] then
            local player = chairs[id].player
			
			local can_hu,jiang = MJLogic.CanChiHu(player:get_hand(), player:get_group(), last_out_card[1], zhengjing, fujing)
            if can_hu == false then
				-- skynet.error("chi_hu fail",id,last_out_card[1])
                return 0
            end

			local done = do_hu_pending_user(pending_user, id, OPER_CHI_HU, last_user)
            if not done then
				-- skynet.error("no do_hu_pending_user")
                return 0
            end
			
			cur_id = id
			dealer:reset_out_card()
			if dealer:is_qianggang() == true then
				player:add_hu_type(MJLogic.HU_QIANGGANG)
				local groups = chairs[last_user].player:get_group()
				for i=#groups,1,-1 do	
					if groups[i][1] == last_out_card[1] and groups[i][2] == last_out_card[1] and groups[i][3] == last_out_card[1] then 
						groups[i] = {last_out_card[1],last_out_card[1],last_out_card[1]}
						-- 减少杠分
						for i=1,MAX_PLAYER_NUM do 
							if i == last_user then 
								chairs[last_user].player:decr_jingfen(4,6)
							else
								chairs[i].player:add_jingfen(4,2)
							end
						end
					end
				end
			end
			if dealer:is_tiandi_hu() == true then
				player:add_hu_type(MJLogic.HU_DIHU)
				player:set_tiandihu(true)
			else
				local bawang = is_bawang(id)
				local hu_types = MJLogic.JudgeHuTypes(player:get_hand(),player:get_group(),zhengjing, fujing, {}, last_out_card[1], bawang, last_out_card[1])
				for i,v in pairs(hu_types) do
					if v ~= MJLogic.HU_NORMAL then 
						player:add_hu_type(v)
					end
				end
			end
			player:set_is_jiepao(1)
			player:set_hu_cards(last_out_card)
			chairs[last_user].player:set_dianpao(true)
			chairs[last_user].player:remove_out_card()
			dealer:set_hu_id(id)
			send_chi_hu(id,last_out_card)
			do_calcu_jingfen(zhengjing, fujing, 3)
			pending_user = {}
			if ruleType == 1 then
				period = PERIOD_ANIMAT
				wait_time = 1
				add_animat(1,OPER_JINGFEN)
				add_animat(1,OPER_RESULT)
			else
				game_result()
			end
			
           -- do_oper_chi_hu(id,last_out_card)
            return 1
        end
        return 0
    end
    local function oper_hu(id,data)
        if chairs[id] then
            local player = chairs[id].player
            if MJLogic.CanZimoHu(player:get_hand(), player:get_group(), zhengjing, fujing) == 0 then
                return 0
            end
			
			local hands = player:get_hand()
			local hu_card = hands[#hands]
			player:set_hu_cards({hu_card})
			
			if dealer:is_tiandi_hu() == true then
				player:add_hu_type(MJLogic.HU_TIANHU)
				player:set_tiandihu(true)
			else
				local bawang = is_bawang(id)
				local hu_types = MJLogic.JudgeHuTypes(player:get_hand(),player:get_group(),zhengjing, fujing, {}, hu_card, bawang, nil)
				for i,v in pairs(hu_types) do
					if v ~= MJLogic.HU_NORMAL then 
						player:add_hu_type(v)
					end
				end
				
				player:add_hu_type(MJLogic.HU_ZIMO)
				if dealer:is_gangkai() == true then 
					player:add_hu_type(MJLogic.HU_GANGKAI)
				end
			end
			player:set_zimo(true)
			player:delete_hand_card({hu_card})
			send_hu(id,hu_card)
			dealer:set_hu_id(id)
			do_calcu_jingfen(zhengjing, fujing, 3)
			cur_id = id
			if ruleType == 1 then
				period = PERIOD_ANIMAT
				wait_time = 1
				add_animat(1,OPER_JINGFEN)
				add_animat(1,OPER_RESULT)
			else
				game_result()
			end
            return 1
        end
        return 0
    end
	
	local function oper_huitou_jingfen()
		local packet = {
			cmd 	= NetCmd.S2C_MJ_JINGFEN,
			dizhu = dizhu,
			players = {},
			title	= 2,
		}
		
		local houtai_card = dealer:get_houtou_zhengjing()
		if houtai_card then
			local houtai_fucard = MJLogic.NextCard(houtai_card)
			packet.houtou_zhengjing = houtai_card
			packet.houtou_fujing = houtai_fucard
			if has_cal_huitou == false then 
				do_calcu_jingfen(houtai_card, houtai_fucard,2)	
				has_cal_huitou = true
			end
		else
			return
		end
		
		for i=1,MAX_PLAYER_NUM do
			local player = chairs[i].player
			local cards ={}
			local cards2 = {}
			local huitou_cards = {}
			local houtai_fucard = MJLogic.NextCard(houtai_card)
			for i,v in ipairs(player:get_hand()) do 
				if v == houtai_card then 
					table.insert(cards,v)
				elseif v == houtai_fucard then 
					table.insert(cards2,v)
				end
			end
			player:set_huitou_zhengjing_num(#cards)
			player:set_huitou_fujing_num(#cards2)
			local tmp = {
				index	= i,
				uid		=	chairs[i].uid, 
				name	=	chairs[i].name, 
				head	=	chairs[i].head, 
				sex 	=	chairs[i].sex,
				ip		=	chairs[i].ip, 
				huitou_fen 			= player:get_jingfen(2),
				huitou_zhengjing_num 	= #cards,
				huitou_fujing_num 		= #cards2,
				total_score 		= player:get_total_score(),
			}
			tmp.score = (tmp.huitou_fen) * dizhu
			packet.players[i] = tmp
		end
		send_all(packet)
		dealer:add_log_order(packet)
	end
	
	local function oper_jingfen()
		xia_zhengjing = dealer:show_rand_card(dealer:get_rest_cards_count())
		xia_fujing = MJLogic.NextCard(xia_zhengjing)
		
		local packet = {
			cmd = NetCmd.S2C_MJ_JINGFEN,
			xia_zhengjing = xia_zhengjing,
			xia_fujing = xia_fujing,
			dizhu = dizhu,
			players = {},
			title	= 1,
		}
		
		do_calcu_jingfen(xia_zhengjing, xia_fujing,1)
		
		local houtai_card = dealer:get_houtou_zhengjing()
		if houtai_card then
			local houtai_fucard = MJLogic.NextCard(houtai_card)
			packet.houtou_zhengjing = houtai_card
			packet.houtou_fujing = houtai_fucard
			if has_cal_huitou == false then 
				do_calcu_jingfen(houtai_card, houtai_fucard,2)	
				for i=1,MAX_PLAYER_NUM do
					local player = chairs[i].player
					local cards ={}
					local cards2 = {}
					for i,v in ipairs(player:get_hand()) do 
						if v == houtai_card then 
							table.insert(cards,v)
						elseif v == houtai_fucard then 
							table.insert(cards2,v)
						end
					end
					player:set_huitou_zhengjing_num(#cards)
					player:set_huitou_fujing_num(#cards2)
				end
			end
		end
		
		for i=1,MAX_PLAYER_NUM do
			local player = chairs[i].player
			local cards ={}
			local cards2 = {}
			for i,v in ipairs(player:get_hand()) do 
				if v == xia_zhengjing then 
					table.insert(cards,v)
				elseif v == xia_fujing then 
					table.insert(cards2,v)
				end
			end
			
			for i,group in ipairs(player:get_group()) do 
				for j,v in ipairs(group) do 
					if v == xia_zhengjing then 
						table.insert(cards,v)
					elseif v == xia_fujing then 
						table.insert(cards2,v)
					end
				end
			end
			
			for j,v in ipairs(player:get_out_cards()) do 
				if v == xia_zhengjing then 
					table.insert(cards,v)
				elseif v == xia_fujing then 
					table.insert(cards2,v)
				end
			end
			
			local tmp = {
				index	= i,
				uid		=	chairs[i].uid, 
				name	=	chairs[i].name, 
				head	=	chairs[i].head, 
				sex 	=	chairs[i].sex,
				ip		=	chairs[i].ip, 
				xiajing_fen 			= player:get_jingfen(1),
				huitou_fen 				= player:get_jingfen(2),
				xia_zhengjing_num 		= #cards,
				xia_fujing_num 			= #cards2,
				huitou_zhengjing_num 	= player:get_huitou_zhengjing_num(),
				huitou_fujing_num 		= player:get_huitou_fujing_num(),
				total_score 			= player:get_total_score(),
			}
			tmp.score = (tmp.xiajing_fen + tmp.huitou_fen ) * dizhu
			packet.players[i] = tmp
		end
		send_all(packet)
		dealer:add_log_order(packet)
	end

    local function action_round( id,oper,data )     -- 行动等待
        local ret = 0
        if oper == OPER_OUT_CARD then
            ret = oper_out_card(id,data)
        elseif oper == OPER_GANG then
            ret = oper_gang(id,data)
        elseif oper == OPER_HU then
            ret = oper_hu(id,data)
        end
        return ret
    end

    local function pending_round( id,oper,data )    -- 悬决等待
        local ret = 0
        if oper == OPER_PENG then
            ret = oper_peng(id,data)
        elseif oper == OPER_GANG then
            ret = oper_gang(id,data)
        elseif oper == OPER_CHI_CARD then
            ret = oper_chi_card(id,data)
        elseif oper == OPER_CHI_HU then
            ret = oper_chi_hu(id,data)
        end
        return ret
    end

	local function animat_round(oper)
		if oper == OPER_JINGFEN then
			oper_jingfen()
		elseif oper == OPER_HUITOU then 
			oper_huitou_jingfen()
		elseif oper == OPER_RESULT then
			game_result()
		end
	end
	
    -- 由庄开始
    local function oper_card(id,oper,data,b_end)
		
        if b_end then
            -- skynet.error("oper_card force end")
            if timer_id then del_skynet_timer(timer_id) end
            timer_id = nil
            return
        end
        local ret = 0
		if oper == OPER_FINISH_ANIMAT then
			if #animats == 0 then
				period = PERIOD_ACTION
				wait_time = OUT_CARD_TIME
			else
				local animat = animats[1]
				table.remove(animats, 1);
				animat_round(animat[2])
				period = PERIOD_ANIMAT
				wait_time = animat[1]
			end
			ret = 1
        elseif oper == OPER_EMPTY then
			if #pending_user == 0 then
				return
			end	
			if #pending_user > 0 then
				local tmp_actions = {}
				for i=#pending_user,1,-1 do	
					if pending_user[i].id == id then
						if pending_user[i].oper == OPER_CHI_HU then 
							chairs[id].player:set_louhu_score(true)
						elseif pending_user[i].oper == OPER_PENG then 
							chairs[id].player:add_cant_peng_card(last_out_card[1])
						end
						table.insert(tmp_actions,pending_user[i].oper)
						table.remove(pending_user, i)
					end
				end	
				
				local packet = {
					cmd = NetCmd.S2C_PASS,
					chair_id = id,
					actions = tmp_actions,
				}
				dealer:add_log_order(packet)
			end
			if #pending_user == 0 and period == PERIOD_PENDING then
				do_draw_card(next_id(cur_id))           
			end		
            ret = 1
        elseif period == PERIOD_ACTION then				-- 出牌
            if cur_id == id then
                ret = action_round(id,oper,data)
            end
        elseif period == PERIOD_PENDING then
			if oper == 	OPER_CAN_CHI_CARD then	-- 判断能不能吃牌
				if #pending_user == 0 then
					return
				end	
				
				if #pending_user > 0 then
					local need_uid = pending_user[#pending_user].id
					
					if need_uid ~= id then
						return;
					end	
						
					-- 查询组合
					local arr = {}
					local player = chairs[id].player
					MJLogic.CanChi(player:get_hand(), last_out_card[1], arr)
					if #arr > 0 then
						send_can_chi_card(id,arr)	
					end
				end
				return
			else
				if cur_id ~= id and get_pending_access(oper) then
					ret = pending_round(id,oper,data)
				end	
			end
        end
        
        if ret == 0 then  
            return
        end

        if timer_id then
            del_skynet_timer(timer_id)
            timer_id = nil
        end

        if status == STATUS_FREE then
            return
        end
		
		if status == STATUS_RESULT then --结束，不要发命令了
			if timer_id then del_skynet_timer(timer_id) end
			timer_id = nil
		else
			timer_id = add_skynet_timer(wait_time,timer_cb)
			if period ~= PERIOD_ANIMAT then
				send_cool_down(cur_id, wait_time, pending_user)
			end
		end
    end
	
	local function judge_tianhu()
		local host_id = dealer:get_host()
		local player = chairs[host_id].player
		
		if MJLogic.GetCanGang(player:get_hand(), player:get_group(), player:get_cant_gang_card()) then
			add_pending_user(pending_user, host_id, OPER_GANG)
		end
		
		if MJLogic.CanZimoHu(player:get_hand(), player:get_group(), zhengjing, fujing) == true then 
			add_pending_user(pending_user, host_id, OPER_HU)
		end
	end
	
    timer_cb = function()       
		if period == PERIOD_ANIMAT then
			oper_card(cur_id, OPER_FINISH_ANIMAT)
        elseif period == PERIOD_ACTION then 	
            local player = chairs[cur_id].player
			local cards = player:get_hand()
			oper_card(cur_id,OPER_OUT_CARD,{cards[#cards]})
        elseif period == PERIOD_PENDING then
            oper_card(cur_id,OPER_EMPTY) 	
        end
    end
    g_oper_card = oper_card
	judge_tianhu()
    timer_id = add_skynet_timer(wait_time,timer_cb)
	if period ~= PERIOD_ANIMAT then
		send_cool_down(cur_id,wait_time, pending_user)
	end
	
end

function calcu_score(i,hu_types)
	--local jingfen = calcu_jingfen(i, zhengjing, fujing)
	local fan = 1
	local add_other = 0
	--MJLogic.sortDESC(hu_types)
	for j,hu_type in ipairs(hu_types) do
		if hu_type == MJLogic.HU_TIANHU or hu_type == MJLogic.HU_DIHU then
			fan = 20
			--fan = 0
			for k,hu in ipairs(hu_types) do
				if hu == MJLogic.HU_JINGDAO then 
					fan = 40
				end
			end
			return 1,fan,add_other
		end
		
		if hu_type == MJLogic.HU_DEIZHONGDEI then 
			fan = fan * 4
			add_other = 5
		elseif hu_type == MJLogic.HU_DEIGUO then 
			fan = fan * 2
			add_other = 5
		elseif hu_type == MJLogic.HU_QXSHISANLAN then 
			fan = fan * 4
		else	
			fan = fan * 2
		end	
	end
	return nil, fan, add_other
end

function game_result(need_finish) 

	status = STATUS_RESULT
	local packet = {
		cmd = NetCmd.S2C_RESULT,
		cur_ju	= cur_ju,
		status = status,
		hu_users = {},
		is_jiesan = need_finish
	}
	
	local hu_id = dealer:get_hu_id()
	if hu_id ~= 0 then 
		local player = chairs[hu_id].player
		--local jingfen = 0
		local fan = 1
		local add_other = 0
		local tianhu = nil
		local hu_types = player:get_hu_types()
		tianhu, fan,add_other = calcu_score(hu_id,hu_types)
		local cur_host_id = dealer:get_host()
		local score = fan
		dealer:set_dibei(fan)
		local hu_is_double = player:get_is_double()
		--player:add_jingfen(3, jingfen)
		for	i=1,MAX_PLAYER_NUM do 
			if i ~= hu_id then 
				local add_score = score
				local other_player = chairs[i].player 
				
				-- 庄家
				if not tianhu and (i == cur_host_id or hu_id == cur_host_id) then 
					other_player:add_hu_type(MJLogic.HU_ZHUANGJIA)
					add_score = add_score * 2
				end
				
				-- 点炮
				if not tianhu and other_player:is_dianpao() == true then 
					other_player:add_hu_type(MJLogic.HU_DIANPAO)
					add_score = add_score * 2
					add_score = add_score + add_other
				end
				
				if not tianhu and player:is_zimo() then 
					add_score = add_score * 2
					add_score = add_score + add_other
				end 
				--add_score = add_score + jingfen
				player:add_score(add_score)
				other_player:decr_score(add_score)
			end
		end
		packet.hu_users = {hu_id}
	end


	if dealer:get_is_zhui_zhuang() == 3 then --追庄 给 1分 
        for i=1,MAX_PLAYER_NUM do
            if chairs[i] then
                local player_tmp = chairs[i].player 
				if i == host_id_old then
					player_tmp:add_hu_type(MJLogic.ZHUI_ZHUANG)
					player_tmp:decr_score(15)
				else
					player_tmp:add_score(5)
				end
			end
		end
		dealer:set_is_zhui_zhuang(0)
	end
	
	if not need_finish then 
		for i=1,MAX_PLAYER_NUM do 
			local player = chairs[i].player
			local score = player:get_score()
			player:set_stat(cur_ju, score)
			if score > 0 then 
				player:add_total_score(score)
			else
				player:decr_total_score(score)
			end
		end
	else
		for i=1,MAX_PLAYER_NUM do
			chairs[i].player:set_stat(cur_ju, 0)  --避免流局 没分数
			chairs[i].player:clean_score()
		end
	end
	
	get_all_cards(packet)
	cur_ju = cur_ju + 1
	
	local finish = need_finish and true or nil
	local host_id = 0 --
	if cur_ju > total_ju or finish  then
		finish = true
		get_last_result(packet)	
	else
		if #packet.hu_users == 1 then
			host_id = packet.hu_users[1]
		elseif  #packet.hu_users == 0 then
			host_id = next_id(host_id)
		end
	end
	
	dealer:add_log_order(packet)
	
	if log_data_id == nil then 
		local cur_ju_now = cur_ju - 1
		log_data_id= skynet.call("MG_SAVE", "lua", "save_logdata",room_id,cur_ju_now, dealer:get_log_data())
	end
	packet.log_data_id = log_data_id
	
	send_all(packet)
	
	if cur_ju == 2 and finish ~= true then  
		local cost_card = skynet.call("MG_SAVE", "lua", "get_config_cost_gold_conf", 'MAJAING_HALL'..total_ju)
		if cost_card > 0 then
			if chairs[1].uid then 
				skynet.call("MG_SAVE", "lua", "update_gold", {gameType = 104,total_ju = total_ju, room_id = room_id, friend_id = friend_id,uid = chairs[1].uid, card = -cost_card, card_add = 0, card_ex = 0}, Common.NANCMJROOM)

				skynet.send(chairs[i].addr, "lua", "send_user_data")
			end
		end
	end
	
	if finish then 
		reset_game(host_id, finish)
	else
		for i=1,MAX_PLAYER_NUM  do
			chairs[i].player:set_ready(false)
		end
		dealer:set_host(host_id)
	end 
	log_data_id = nil
	dealer:clean_log_data() 
end

